/*
 * Mutex.cpp
 *
 *  Created on: 2011/05/28
 *      Author: naoki
 */

#include "NTEngine/NTEngine.h"
#include "NTEngine/Threading/Mutex.h"
#include "NTEngine/Base/ReferenceCounter.h"

#include <pthread.h>

namespace NTEngine {
namespace Threading {

// 2値セマフォ
// fast mutexならばほぼ2値セマフォだが
// 専用の変数を用意することでunlockで2重unlockのチェックをする
class Mutex::Impl : public ReferenceCounter {
public:
	Impl() : mCount( 1 ) {
		pthread_mutex_init( &mMutex, NULL );
		pthread_cond_init( &mCondition, NULL );
	}

	~Impl() {
		pthread_cond_destroy( &mCondition );
		pthread_mutex_destroy( &mMutex );
	}

	void lock() {
		pthread_mutex_lock( &mMutex );
		while ( mCount == 0 ) { // セマフォ値が0のときは待つ(block)
			pthread_cond_wait( &mCondition, &mMutex );
		}
		mCount = 0; // セマフォ値を0(ロックする)
		pthread_mutex_unlock( &mMutex );
	}

	void unlock() {
		int prev; // 直前の値をチェックする用
		pthread_mutex_lock( &mMutex );
		prev = mCount;
		mCount = 1; // セマフォ値を1にする(ロックを解除)
		pthread_cond_signal( &mCondition );
		pthread_mutex_unlock( &mMutex );
		HARD_ASSERT( prev == 0 );
	}

	int mCount;
	pthread_mutex_t mMutex;
	pthread_cond_t mCondition;
};

Mutex Mutex::create() {
	Mutex ret;
	ret.mImpl = NEW Impl();
	return ret;
}

void Mutex::lock() {
	mImpl->lock();
}

void Mutex::unlock() {
	mImpl->unlock();
}

#define TYPE Mutex
#include "NTEngine/Base/Impl/ReferenceCounterTemplate.h"

} // namespace Threading
} // namespace NTEngine
